package org.cliff.message.platform.core.service;

import org.cliff.message.platform.core.domain.WorkerNode;
import org.cliff.message.platform.core.mapper.WorkerNodeMapper;
import com.xfvape.uid.worker.WorkerIdAssigner;
import com.xfvape.uid.worker.WorkerNodeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.xfvape.uid.utils.DockerUtils;
import com.xfvape.uid.utils.NetUtils;

import javax.annotation.Resource;
import java.util.Random;

/**
 * Represents an implementation of {@link WorkerIdAssigner},
 * the worker id will be discarded after assigned to the UidGenerator
 * 由于百度UID 18年后就没有更新，而且使用的是老的Mybatis编程模式，
 * 为了使消息平台组件更独立，采用新Mybatis编程方式重写DAO
 * @author 刘文
 * @date 2023/10/11
 */
@Service
public class DisposableWorkerIdAssigner implements WorkerIdAssigner {
    private static final Logger LOGGER = LoggerFactory.getLogger(com.xfvape.uid.worker.DisposableWorkerIdAssigner.class);

    @Resource
    private WorkerNodeMapper workerNodeMapper;


    private long maxWorkerId = 65535;

    public void setWorkerIdBits(int workerIdBits) {
        this.maxWorkerId = ~(-1L << workerIdBits);
    }

    /**
     * Assign worker id base on database.<p>
     * If there is host name & port in the environment, we considered that the node runs in Docker container <br>
     * Otherwise, the node runs on an actual machine.
     * </p>
     * @return assigned worker id
     */
    @Transactional
    public long assignWorkerId() {
        // build worker node entity
        WorkerNode workerNodeEntity = buildWorkerNode();

        // add worker node for new (ignore the same IP + PORT)
        workerNodeMapper.insert(workerNodeEntity);
        LOGGER.info("Add worker node:" + workerNodeEntity);

        return workerNodeEntity.getId() % maxWorkerId;
    }

    /**
     * Build worker node entity by IP and PORT
     */
    private WorkerNode buildWorkerNode() {
        WorkerNode workerNodeEntity = new WorkerNode();
        if (DockerUtils.isDocker()) {
            workerNodeEntity.setType(WorkerNodeType.CONTAINER.value());
            workerNodeEntity.setHostName(DockerUtils.getDockerHost());
            workerNodeEntity.setPort(DockerUtils.getDockerPort());

        } else {
            workerNodeEntity.setType(WorkerNodeType.ACTUAL.value());
            workerNodeEntity.setHostName(NetUtils.getLocalAddress());
            workerNodeEntity.setPort(System.currentTimeMillis() + "-" + new Random().nextInt(100000));
        }

        return workerNodeEntity;
    }

}